package org.fuys.coder.infrastructure.util;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.fuys.coder.common.constants.ResultMessageConstants;
import org.fuys.coder.common.exception.BusinessException;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @projectName: fuys-low-coder
 * @package: org.fuys.coder.common.util
 * @className: RedisUtil
 * @author: WangJing
 * @description: redis工具类
 * @date: 2024/6/18 20:39
 * @version: 1.0
 */
@Service
@Slf4j
public class RedisUtil {
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Resource
    private LuaScriptLoader luaScriptLoader;

    private ObjectMapper objectMapper=new ObjectMapper();

    public RedisTemplate<String,Object> getRedisTemplate(){
        return this.redisTemplate;
    }

    public void commonSet(String key,Object value){
        redisTemplate.opsForValue().set(key,value);
    }

    public void commonSet(String key,Object value,long expire,TimeUnit timeUnit){
        redisTemplate.opsForValue().set(key, value, expire,timeUnit);
    }
    public Object commonGet(String key){
        return redisTemplate.opsForValue().get(key);
    }

    public void expire(String key,long time, TimeUnit timeUnit){
        redisTemplate.expire(key,time,timeUnit);
    }

    public void deleteKey(String key){
        redisTemplate.delete(key);
    }

    public void hashSet(String hashKey, String field, Object value){
        redisTemplate.opsForHash().put(hashKey, field,value);
    }

    public Object hashGet(String hashKey, String field){
        return redisTemplate.opsForHash().get(hashKey,field);
    }

    public void setList(String key, List value){
        redisTemplate.opsForList().leftPushAll(key,value);
    }

    public List<Object> getList(String key){
        return redisTemplate.opsForList().range(key,0,-1);
    }
    public void addList(String key, Object value){
        redisTemplate.opsForList().leftPush(key,value);
    }



    public void zsetPutByPipeLine(String redisKey, List data, List<Double> weights) {
        if(ObjectUtils.isEmpty(data)||ObjectUtils.isEmpty(weights)){
            throw new BusinessException(ResultMessageConstants.ILLEGAL_DATA_FORMAT+"要插入到Redis的zset数据不能为空");
        }
        if(data.size()!=weights.size()){
            throw new BusinessException(ResultMessageConstants.ILLEGAL_DATA_FORMAT+"zset权重和具体内容数量应一致");
        }
        byte[] bytes=redisKey.getBytes();
        redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
                for (int i = 0; i < data.size(); i++) {
                    operations.opsForZSet().add((K)redisKey,(V)data.get(i),weights.get(i));
                }
                return null;
            }
        });
    }

    public Set<ZSetOperations.TypedTuple<Object>> zsetGetMembersAndScore(String s, Integer count) {
        Set<ZSetOperations.TypedTuple<Object>> typedTuples = redisTemplate.opsForZSet().rangeByScoreWithScores(s, -Double.MAX_VALUE, Double.MAX_VALUE, 0, count);
        return typedTuples;
    }

    public void zsetLimitCount(String key,int maxCount){
        final ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();
        final Long size = zSetOperations.size(key);
        if(size!=null&&size>maxCount){
            final Set<Object> range = zSetOperations.range(key, 0, size - maxCount);
            zSetOperations.remove(key,range);
        }
    }

    public void zsetAdd(String key,Object value,Double score){
        redisTemplate.opsForZSet().add(key,value,score);
    }

    public void multipleKeyListInsert(List<String> keys, Object data) {
        //todo 暂时放弃使用sha值来执行lua脚本了 没办法搞定序列化
        String scriptName="MulitiKeyListInsert.lua";
        final DefaultRedisScript<String> script = luaScriptLoader.getScript(scriptName);
        if(ObjectUtils.isEmpty(script)){
            throw new BusinessException(ResultMessageConstants.SCRIPT_NOT_FOUND+scriptName);
        }
        final Object execute = redisTemplate.execute(script, keys, data);
    }

    public void removeListItems(String key,List<Integer> indexes) {
        String scriptName="RemoveListItemsByIdx.lua";
        final DefaultRedisScript<String> script = luaScriptLoader.getScript(scriptName);
        if(ObjectUtils.isEmpty(script)){
            throw new BusinessException(ResultMessageConstants.SCRIPT_NOT_FOUND+scriptName);
        }
        final List<String> collect = indexes.stream().map(Object::toString).collect(Collectors.toList());

        final Object execute = redisTemplate.execute(script, collect, key);

    }

    public void removeListItem(Integer index){

    }
}
